package jsr292.java.lang.invoke;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import jsr292.weaver.opt.Optimizer;

public abstract class CallSite {
    private final MethodType type;
    public MethodHandle target;  // used by the backport runtime
    public int counter;          // used by the backport runtime
    
    // all mutations should appear under the global lock LOCK
    final HashSet<Class<?>> callerSet = new HashSet<Class<?>>();

    ArrayList<CallSite> dependencies;     // exactInvokers dependencies, can be used only in a synchronized block callerSet
    
    static final Object LOCK = new Object();
    
    
    CallSite(MethodType type) {
        this.type = type;
    }
    
    CallSite(MethodHandle target) {
        this.type = target.type();
        this.target = target;
    }
    
    public MethodType type() {
        return type;
    }
    
    public MethodHandle getTarget() {
        return target;
    }

    public void setTarget(MethodHandle target) {
        target.getClass(); // provoke NPE 
        if (this.target != null && type != target.type())
            throw new WrongMethodTypeException(String.valueOf(target) + " waiting for "+type);
        
        // if call site is already optimized, need de-optimization  
        HashSet<Class<?>> retransformClasses = new HashSet<Class<?>>();
        synchronized(LOCK) {
            this.target = target;
            
            // register invalidation of caller classes
            invalidateAndPopulateCallerClassSet(retransformClasses);
            
            // register invalidation of caller classes of dynamic invokers built on this callsite
            synchronized(callerSet) {
                if (dependencies != null) {
                    for(CallSite callSite: dependencies) {
                        callSite.invalidateAndPopulateCallerClassSet(retransformClasses);
                    }
                }
            }
            
            if (!retransformClasses.isEmpty()) {
                Optimizer.retransform(retransformClasses);
            }
        }
    }
    
    public abstract MethodHandle dynamicInvoker();
    
    // injected after bootstrap, needed to be able to retransform call sites
    final void injectCallerClass(Class<?> callerClass) {
        // LOCK is already held in BootstrapCallSite.bootstrapCallSite()
        callerSet.add(callerClass);
    }
    
    final void invalidateAndPopulateCallerClassSet(Set<Class<?>> callerSet) {
        // if call site is already optimized, need de-optimization
        if (counter < 0) {
          counter = Optimizer.OPTIMIZER_INDY_DEOPT_THRESHOLD;
          synchronized(LOCK) {
              callerSet.addAll(this.callerSet);
          }
        }
    }
}
